﻿using System;
using System.Collections;
using System.Collections.Generic;



namespace Crocodile.LangUtils
{
    /// <summary>
    /// Numbering enumerable items.
    /// </summary>
    public class NumberedEnumerable<S,T> : IEnumerable<T>
    {
        private readonly IEnumerable<S> source;
        private readonly Func<int, S, T> numberator;

        /// <summary>
        /// Creates 
        /// </summary>
        /// <param name="source">source sequence.</param>
        /// <param name="numberator">function that numberates each item.</param>
        public NumberedEnumerable(IEnumerable<S> source, Func<int, S, T> numberator)
        {
            this.source = source;
            this.numberator = numberator;
        }


        public IEnumerator<T> GetEnumerator()
        {
            return new NumberingEnumerator(source.GetEnumerator(), numberator);
        }


        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }


        private class NumberingEnumerator : IEnumerator<T>
        {
            private readonly IEnumerator<S> sen;
            private readonly Func<int,S,T> numberator;

            private int curr = 0;
            private bool eof = false;

            public NumberingEnumerator(IEnumerator<S> sen, Func<int, S, T> numberator)
            {
                this.sen = sen;
                this.numberator = numberator;
            }


            public void Reset()
            {
                sen.Reset();
                curr = 0;
                eof = false;
            }


            public bool MoveNext()
            {
                if (!eof)
                {
                    curr++;
                    bool ok = sen.MoveNext();
                    eof = !ok;
                    return ok;
                }
                else
                {
                    return false;
                }
            }


            public T Current
            {
                get { return numberator(curr, sen.Current); }
            }


            object IEnumerator.Current
            {
                get { return Current; }
            }


            public void Dispose()
            {
                sen.Dispose();
                eof = true;
            }

        }
    }


    public static class NumberedEnumerableExt
    {
        public static IEnumerable<T> Numberate<S,T> (this IEnumerable<S> sequence, Func<int, S, T> numberator)
        {
            return new NumberedEnumerable<S,T>(sequence, numberator);
        }
    }
}
